<template>
  <el-card class="my-card h-full flex-grow">
    <template #header>
      <h3 class="m-0 px-7 shrink-0 flex items-center justify-between">
        <span>思维导图预览</span>
        <!-- 展示在右上角 -->
        <el-button
          v-show="isEnd"
          size="small"
          class="liner-btn-opacity liner-btn"
          type="primary"
          @click="downloadImage"
        >
          <template #icon>
            <el-icon>
              <Download />
            </el-icon>
          </template>
          下载图片
        </el-button>
      </h3>
    </template>

    <div ref="contentRef" class="hide-scroll-bar h-full box-border">
      <!--展示 markdown 的容器，最终生成的是 html 字符串，直接用 v-html 嵌入-->
      <div v-if="isGenerating" ref="mdContainerRef" class="wh-full overflow-y-auto">
        <div class="flex flex-col items-center justify-center" v-html="html"></div>
      </div>

      <div ref="mindMapRef" class="wh-full posr">
        <svg
          ref="svgRef"
          :style="{ height: `${contentAreaHeight}px`, background: '#fff' }"
          class="w-full"
        ></svg>
        <div ref="toolBarRef" class="absolute bottom-[10px] right-5"></div>
      </div>
    </div>
  </el-card>
</template>

<script setup>
import { onMounted, onUnmounted, ref, reactive, useTemplateRef, watch, nextTick } from "vue";
import { Markmap } from "markmap-view";
import { Download } from "@element-plus/icons-vue";
import { Transformer } from "markmap-lib";
import { Toolbar } from "markmap-toolbar";
import markdownit from "markdown-it";
import download from "@/utils/download";
import { useMessage } from "@/hooks/web/useMessage";
const md = markdownit();
const message = useMessage(); // 消息弹窗

const props = defineProps({
  generatedContent: {
    type: String,
    default: "",
  },
  isEnd: {
    type: Boolean,
    default: false,
  }, // 是否结束
  isGenerating: {
    type: Boolean,
    default: false,
  }, // 是否正在生成
  isStart: {
    type: Boolean,
    default: false,
  }, // 开始状态，开始时需要清除 html
});
const contentRef = ref(); // 右侧出来 header 以下的区域
const mdContainerRef = ref(); // markdown 的容器，用来滚动到底下的
const mindMapRef = ref(); // 思维导图的容器
const svgRef = ref(); // 思维导图的渲染 svg
const toolBarRef = ref(); // 思维导图右下角的工具栏，缩放等
const html = ref(""); // 生成过程中的文本
const contentAreaHeight = ref(0); // 生成区域的高度，出去 header 部分
let markMap = null;
const transformer = new Transformer();

onMounted(() => {
  contentAreaHeight.value = contentRef.value?.clientHeight || 0; // 获取区域高度
  /** 初始化思维导图 **/
  try {
    // 创建工具栏并排除放大按钮
    const toolbarConfig = {
      buttons: ["zoomIn", "zoomOut", "fit"], // 排除 'zoomIn'
    };
    markMap = Markmap.create(svgRef.value);
    const tool = Toolbar.create(markMap, toolbarConfig);
    tool.setItems(toolbarConfig.buttons);
    const { el } = tool;
    toolBarRef.value?.append(el);
    nextTick(update);
  } catch (e) {
    message.error("思维导图初始化失败");
  }
});

watch(props, ({ generatedContent, isGenerating, isEnd, isStart }) => {
  // 开始生成的时候清空一下 markdown 的内容
  if (isStart) {
    html.value = "";
  }
  // 生成内容的时候使用 markdown 来渲染
  if (isGenerating) {
    html.value = md.render(generatedContent);
  }
  // 生成结束时更新思维导图
  if (isEnd) {
    update();
  }
});

/** 更新思维导图的展示 */
const update = () => {
  if (!props.generatedContent) {
    console.log(
      "%c无内容强行进行内部g标签清理，去除默认横线",
      "color: white; background-color: #f1738e; padding: 2px 5px; border-radius: 2px"
    );
    svgRef.value.querySelector("g").innerHTML = "";
    return;
  }
  try {
    const { root } = transformer.transform(processContent(props.generatedContent));
    markMap?.setData(root);
    markMap?.fit();
  } catch (e) {
    console.error(e);
  }
};

/** 处理内容 */
const processContent = (text) => {
  const arr = [];
  const lines = text.split("\n");
  for (let line of lines) {
    if (line.indexOf("```") !== -1) {
      continue;
    }
    line = line.replace(/([*_~`>])|(\d+\.)\s/g, "");
    arr.push(line);
  }
  return arr.join("\n");
};

/** 下载图片：download SVG to png file */
const downloadImage = () => {
  const svgElement = mindMapRef.value;
  // 将 SVG 渲染到图片对象
  const serializer = new XMLSerializer();
  const source = `<?xml version="1.0" standalone="no"?>\r\n${serializer.serializeToString(
    svgRef.value
  )}`;
  const base64Url = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(source)}`;
  download.image({
    url: base64Url,
    canvasWidth: svgElement?.offsetWidth,
    canvasHeight: svgElement?.offsetHeight,
    drawWithImageSize: false,
  });
};

defineExpose({
  scrollBottom() {
    mdContainerRef.value?.scrollTo(0, mdContainerRef.value?.scrollHeight);
  },
});
</script>
<style lang="scss" scoped>
.hide-scroll-bar {
  -ms-overflow-style: none;
  scrollbar-width: none;

  &::-webkit-scrollbar {
    width: 0;
    height: 0;
  }
}

.my-card {
  display: flex;
  flex-direction: column;

  :deep(.el-card__body) {
    box-sizing: border-box;
    flex-grow: 1;
    overflow-y: auto;
    padding: 0;
    @extend .hide-scroll-bar;
  }
}

// markmap的tool样式覆盖
:deep(.markmap) {
  width: 100%;
}

:deep(.mm-toolbar-brand) {
  display: none;
}

:deep(.mm-toolbar) {
  display: flex;
  flex-direction: row;
}
</style>
